
#include "libmri.h"
#include "l1spirit_config.h"
#include <string.h>
#include <stdlib.h>

#include <iostream>
using namespace std;

l1spirit_config::l1spirit_config
		(int ks0, int ks1, int ks2,
		 spirit_calib_t calib,
		 float calib_reg, int calib_nr,
		 float calib_tau, int pix_eig, int pix_qrit,
		 bool l1gpu, float l1_t_start, float l1_t_end, int l1_iters)
{
	ksize = new int[3];
	ksize[0] = ks0; ksize[1] = ks1; ksize[2] = ks2;

	calib_n_readouts = calib_nr;

	calib_algorithm = calib;

	calib_tychonov = calib_reg;
	calib_threshold = calib_tau;
	calib_pixel_n_eig = pix_eig;
	calib_pixel_n_qrit = pix_qrit;


	l1min_on_gpu = l1gpu;

	l1_threshold_start = l1_t_start;
	l1_threshold_end = l1_t_end;
	l1spirit_iterations = l1_iters;

	timing_info = false;
	verbose_info = false;

	for (int i = 0; i < n_debug_points; ++i){
		debug[i] = 0;
	}

	debug_slice = -1;
}

l1spirit_config::~l1spirit_config (){
	if (ksize) delete [] ksize;
}

void parse_l1spirit_args (l1spirit_config& config, int argc, char *argv[])
{
	int nksize = -1;
	float *l1_t = 0;
	int n_l1_t = -1;

	bool debug = false;
	bool printoptions = false;
	char *calib_alg = 0;

	struct cmdline_arg *args [] = {

		new cmdline_arg_t <bool>  ("debug", debug),
		new cmdline_arg_t <bool>  ("help", printoptions),

		new cmdline_arg_t <char*> ("calib_alg", calib_alg),

		new    list_arg_t <int>   ("ksize",     config.ksize, nksize, "x"),
	    new cmdline_arg_t <float> ("calib_tau", config.calib_threshold),

		new cmdline_arg_t <int>   ("pix_n_eig", config.calib_pixel_n_eig),
		new cmdline_arg_t <int>  ("pix_n_qrit", config.calib_pixel_n_qrit),

		new cmdline_arg_t <float> ("calib_tyc", config.calib_tychonov),
		new cmdline_arg_t <int>   ("calib_nro", config.calib_n_readouts),


		new cmdline_arg_t <bool>  ("l1_gpu",    config.l1min_on_gpu),

		new    list_arg_t <float> ("l1_thresh", l1_t, n_l1_t, ":"),
		new cmdline_arg_t <int>   ("l1_iters",  config.l1spirit_iterations),


		new cmdline_arg_t <bool>  ("timing",    config.timing_info),
		new cmdline_arg_t <bool>  ("verbose",   config.verbose_info),
		new cmdline_arg_t <int>   ("debug_slice", config.debug_slice),

#define debugging_arg(name) \
	new cmdline_arg_t<char*> (#name, config.debug[l1spirit_config::debug_##name]),

		debugging_arg(data)
		debugging_arg(density)
        debugging_arg(scale)
        debugging_arg(wavplan)
        debugging_arg(mask)
        debugging_arg(calib)
        debugging_arg(calib_k3d)
        debugging_arg(calib_k2d)
        debugging_arg(data_roifft)
        debugging_arg(data_soln)
        debugging_arg(data_rofft)
        debugging_arg(l1s_x0)
        debugging_arg(l1s_kern)
        debugging_arg(l1s_fft)
        debugging_arg(l1s_spir)
        debugging_arg(l1s_fwav)
        debugging_arg(l1s_lambda)
        debugging_arg(l1s_thr)
        debugging_arg(l1s_iwav)
        debugging_arg(l1s_ifft)
        debugging_arg(l1s_proj)

#undef debugging_arg
		0
	};

	for (int i = 1; i < argc; ++i)
		get_args (args, argv[i], ",");

	if (0 != l1_t){
		if (n_l1_t == 1){
			config.l1_threshold_start = config.l1_threshold_end = l1_t[0];
		}
		else if (n_l1_t > 1){
			config.l1_threshold_start = l1_t[0];
			config.l1_threshold_end = l1_t[1];
		}
	}

	if (0 != calib_alg){
		if (!strcmp (calib_alg, "posv"))
			config.calib_algorithm = l1spirit_config::posv;
		if (!strcmp (calib_alg, "chol"))
			config.calib_algorithm = l1spirit_config::chol;
		if (!strcmp (calib_alg, "cheev"))
			config.calib_algorithm = l1spirit_config::cheev;
		if (!strcmp (calib_alg, "lanczos"))
			config.calib_algorithm = l1spirit_config::lanczos;
		if (!strcmp (calib_alg, "culanczos"))
			config.calib_algorithm = l1spirit_config::culanczos;
	}

	if (printoptions){
		cout << "Command line options: " << endl;
		for (int i = 0; args[i]; ++i){
			cout << "  " << args[i]->name << " : " <<  args[i]->type_string() << endl;
		}
	}

	if (config.verbose_info){
		config.print();
	}

	for (cmdline_arg **a = args; *a; ++a)
		delete *a;

	if (not debug)
		return;

#define debugging_arg(name) \
	if (not config.debug[l1spirit_config::debug_##name]){ \
		config.debug[l1spirit_config::debug_##name] = strdup(#name);\
	}

	debugging_arg(data)
	debugging_arg(density)
	debugging_arg(scale)
	debugging_arg(wavplan)
	debugging_arg(mask)
	debugging_arg(calib)
	debugging_arg(calib_k3d)
	debugging_arg(calib_k2d)
	debugging_arg(data_roifft)
	debugging_arg(data_soln)
	debugging_arg(data_rofft)

#undef debugging_arg

#define debugging_arg(name) \
	if (not config.debug[l1spirit_config::debug_##name]){ \
		config.debug[l1spirit_config::debug_##name] = strdup(#name "_%d");\
	}

	debugging_arg(l1s_x0)
	debugging_arg(l1s_kern)
	debugging_arg(l1s_fft)
	debugging_arg(l1s_spir)
	debugging_arg(l1s_fwav)
	debugging_arg(l1s_lambda)
	debugging_arg(l1s_thr)
	debugging_arg(l1s_iwav)
	debugging_arg(l1s_ifft)
	debugging_arg(l1s_proj)

#undef debugging_arg
}

void l1spirit_config::print()
{
	cout << "l1 SPIRiT Configuration: " << endl;

	cout << "  Calibration Algorithm: ";
	switch (calib_algorithm){
		case posv: cout << "posv (Deprecated SPIRiT calibration)"; break;
		case chol: cout << "chol (SPIRiT calibration)"; break;
		case cheev: cout << "cheev (Sensitivity Map Eigen-Estimation)"; break;
		case lanczos: cout << "lanczos (Sensitivity Map Eigen-Estimation)"; break;
		case culanczos: cout << "culanczos (GPU Sensitivity Map Eigen-Estimation)"; break;
	}
	cout << endl;

	cout << "  Kernel Size: "
		<< ksize[0] << " x " << ksize[1] << " x " << ksize[2] << endl;

	cout << "  Calib. Readouts: " << calib_n_readouts << endl;

	if (calib_algorithm == posv or calib_algorithm == chol){
		cout << "  Calib. Tychonov: " << calib_tychonov << endl;
	}

	if (calib_algorithm == cheev or calib_algorithm == lanczos){
		cout << "  A-matrix eigenvalue threshold: " << calib_threshold << endl;
		cout << "  # of voxel eigenvectors: " << calib_pixel_n_eig << endl;
		cout << "  # of voxel QR iterations: " << calib_pixel_n_qrit << endl;
	}

	cout << "  l1-minimization on GPUs? " << (l1min_on_gpu ? "yes" : "no") << endl;
	cout << "  l1 Soft Threshold: ";
	if (l1_threshold_start == l1_threshold_end){
		cout << l1_threshold_start << endl;
	}
	else{
		cout << l1_threshold_start << " ... " << l1_threshold_end << endl;
	}
	cout << "  l1-SPIRiT iterations: " << l1spirit_iterations << endl;
}

